/* $Id: AdminClientPersonReviewFacadeImpl.java 
 * Copyright (C) 2011, TRAKHEES
 *  
 * Revision History
 *
 * Date  			Author 		    	Remarks
 * ---------------------------------------------------------------------------------
 *
 */
package ae.pcfc.etks.online.common.facade.provider;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.transaction.annotation.Transactional;

import ae.pcfc.etks.online.common.ConstantsCommon;
import ae.pcfc.etks.online.common.domain.AppMetaData;
import ae.pcfc.etks.online.common.domain.Client;
import ae.pcfc.etks.online.common.domain.ClientServiceRole;
import ae.pcfc.etks.online.common.domain.Service;
import ae.pcfc.etks.online.common.domain.ServiceRole;
import ae.pcfc.etks.online.common.domain.User;
import ae.pcfc.etks.online.common.domain.UserReq;
import ae.pcfc.etks.online.common.domain.UserServiceSecurity;
import ae.pcfc.etks.online.common.domain.manager.AppMetaDataDomainObjectManager;
import ae.pcfc.etks.online.common.domain.manager.ClientDomainObjectManager;
import ae.pcfc.etks.online.common.domain.manager.ProcessStepActionDomainObjectManager;
import ae.pcfc.etks.online.common.domain.manager.ServiceDomainObjectManager;
import ae.pcfc.etks.online.common.domain.manager.SingleClientPersonDomainObjectManager;
import ae.pcfc.etks.online.common.domain.manager.UserDomainObjectManager;
import ae.pcfc.etks.online.common.domain.manager.UserReqDomainObjectManager;
import ae.pcfc.etks.online.common.domain.manager.UserServiceSecurityDomainObjectManager;
import ae.pcfc.etks.online.common.domain.manager.UtilityDomainObjectManager;
import ae.pcfc.etks.online.common.exception.InvalidInputException;
import ae.pcfc.etks.online.common.facade.AdminClientPersonReviewFacade;
import ae.pcfc.etks.online.common.vo.AdminApprovalVO;
import ae.pcfc.etks.online.common.vo.ClientPersonVO;
import ae.pcfc.etks.online.common.vo.SCClientPersonRoleVO;
import ae.pcfc.etks.online.wf.service.client.ClientAdminWF;
import ae.pcfc.etks.online.wf.service.client.vo.ClientAdminCompleteStepVO;


public class AdminClientPersonReviewFacadeImpl extends BaseFacade implements AdminClientPersonReviewFacade{
    	/*
	 * Here we coordinate between the all interacting business domain objects
	 * (BO), transactions are maintained here. use case business validation are
	 * maintained here (that requires DB checks or retrieving data from DB)
	 */    	
	private ClientDomainObjectManager clientDomainObjectManager;	
	private AppMetaDataDomainObjectManager appMetaDataDomainObjectManager;
	private SingleClientPersonDomainObjectManager singleClientPersonDomainObjectManager;
	private ServiceDomainObjectManager serviceDomainObjectManager;
	private UserServiceSecurityDomainObjectManager userServiceSecurityDomainObjectManager;
	private ClientAdminWF clientPersonAdminWF;
	private UtilityDomainObjectManager utilityDomainObjectManager;
	private ProcessStepActionDomainObjectManager processStepActionDomainObjectManager;
	private UserReqDomainObjectManager userReqDomainObjectManager;
	private UserDomainObjectManager userDomainObjectManager;


	public UserDomainObjectManager getUserDomainObjectManager() {
	    return userDomainObjectManager;
	}

	public void setUserDomainObjectManager(
		UserDomainObjectManager userDomainObjectManager) {
	    this.userDomainObjectManager = userDomainObjectManager;
	}

	public UserReqDomainObjectManager getUserReqDomainObjectManager() {
	    return userReqDomainObjectManager;
	}

	public void setUserReqDomainObjectManager(
		UserReqDomainObjectManager userReqDomainObjectManager) {
	    this.userReqDomainObjectManager = userReqDomainObjectManager;
	}


	public UtilityDomainObjectManager getUtilityDomainObjectManager() {
		return utilityDomainObjectManager;
	}

	public void setUtilityDomainObjectManager(
			UtilityDomainObjectManager utilityDomainObjectManager) {
		this.utilityDomainObjectManager = utilityDomainObjectManager;
	}

	public ProcessStepActionDomainObjectManager getProcessStepActionDomainObjectManager() {
		return processStepActionDomainObjectManager;
	}

	public void setProcessStepActionDomainObjectManager(
			ProcessStepActionDomainObjectManager processStepActionDomainObjectManager) {
		this.processStepActionDomainObjectManager = processStepActionDomainObjectManager;
	}

	
	public void setClientDomainObjectManager(
			ClientDomainObjectManager clientDomainObjectManager) {
		this.clientDomainObjectManager = clientDomainObjectManager;
	}

	public void setAppMetaDataDomainObjectManager(
			AppMetaDataDomainObjectManager appMetaDataDomainObjectManager) {
		this.appMetaDataDomainObjectManager = appMetaDataDomainObjectManager;
	}
	
	public void setSingleClientPersonDomainObjectManager(
			SingleClientPersonDomainObjectManager singleClientPersonDomainObjectManager) {
		this.singleClientPersonDomainObjectManager = singleClientPersonDomainObjectManager;
	}

	public void setServiceDomainObjectManager(
			ServiceDomainObjectManager serviceDomainObjectManager) {
		this.serviceDomainObjectManager = serviceDomainObjectManager;
	}
	
	public void setUserServiceSecurityDomainObjectManager(
			UserServiceSecurityDomainObjectManager userServiceSecurityDomainObjectManager) {
		this.userServiceSecurityDomainObjectManager = userServiceSecurityDomainObjectManager;
	}
	
	
	public void setClientPersonAdminWF(ClientAdminWF clientPersonAdminWF) {
		this.clientPersonAdminWF = clientPersonAdminWF;
	}

	@Transactional( readOnly = true )
	public ClientPersonVO getClientPersonByApplicationId(Long applicationId) {
		
		/*ClientReq clientReq =  companyDomainObjectManager.getClientByApplicationId(applicationId);
		
		if(clientReq == null)
			return null;
		
		ClientPersonVO vo = new ClientPersonVO();
		getMapper().map(clientReq, vo);
		
		Iterator<UserReq> iterator = clientReq.getUserReqs().iterator();
		UserReq userReq;
		
		while(iterator.hasNext())
		{
			userReq = iterator.next();
		
			vo.setStrClientPersonNameARB(userReq.getUReqNameArb());
			vo.setStrClientPersonNameENG(userReq.getUReqNameEng());
			vo.setStrUserName(userReq.getUReqUsername());
			vo.setStrEmailAddress(userReq.getUReqEmail());
			vo.setStrMobileNo(userReq.getUReqMobile());
			vo.setStrEmailNotification(userReq.getUReqEmailNotification());
			vo.setStrMobileNotification(userReq.getUReqSmsNotification());
			
			Iterator<UserDocumentsReq> itrUserDocs = userReq.getUserDocumentsReqs().iterator();
			
			UserDocumentsReq userDocumentsReq;
			
			while(itrUserDocs.hasNext())
			{
				userDocumentsReq = itrUserDocs.next();
				vo.setStrUploadDocument(userDocumentsReq.getUdReqUrl());
			}			
		}		
		return vo;*/
		return null;
	}

	/**
	 * reviewRegisterClientPerson
	 * @return 
	 * 
	 * @see ae.pcfc.etks.online.common.facade.AdminClientPersonReviewFacade#reviewRegisterClientPerson(ae.pcfc.etks.online.common.vo.AdminApprovalVO, ae.pcfc.etks.online.common.vo.ClientPersonVO)
	 * 
	 */
	@Transactional
	public ClientAdminCompleteStepVO reviewRegisterClientPerson(AdminApprovalVO adminApprovalVO, ClientPersonVO clientPersonVO) {
    	AppMetaData appMetaData = appMetaDataDomainObjectManager.findByPrimaryKeyProcessAndStepCode(adminApprovalVO
    			.getStrApplicationId().toString(), adminApprovalVO.getProcessCode().getProcessCode(), adminApprovalVO.getStrStepName());

        	if (appMetaData != null) {
        		/************ Storing Meta Data in Tables ***********************/
        	    	if(appMetaDataDomainObjectManager.validateAppMetaDataWithStep(appMetaData,adminApprovalVO.getStrStepName())){
		    	    throw new InvalidInputException();
		    	}
        		String wfStatusEn = appMetaData.getWfStatusEn();
				String wfStatusAr = appMetaData.getWfStatusAr();
			
				getMapper().map(adminApprovalVO, appMetaData);
			
				appMetaData.setWfStatusEn(wfStatusEn);
				appMetaData.setWfStatusAr(wfStatusAr);
        		this.appMetaDataDomainObjectManager.updateAppMetaDataWithActionTaken(appMetaData, adminApprovalVO.getStrStepName());
        		/************** End Store Meta Date ***************************/
        		ClientAdminCompleteStepVO completeStepVO = getCompleteStepVO(adminApprovalVO,clientPersonVO, appMetaData);
        
        		if(clientPersonAdminWF.getFinDeptStepName().equalsIgnoreCase(adminApprovalVO.getStrStepName()) && adminApprovalVO.getStrActionType().equals(ConstantsCommon.APPROVE)){
        		    // If Approved by Finance department, send the approval message to the Ultimus alone and update the application request in the DB as approved
			    	createClientPersonAdmin(adminApprovalVO, clientPersonVO);
			    	UserReq userReq = this.userReqDomainObjectManager.findByApplicationId(adminApprovalVO.getStrApplicationId());
				userReq.setUReqStatus(ConstantsCommon.APPROVE);
				this.userReqDomainObjectManager.update(userReq);
			    	appMetaData.setWfStatus(ConstantsCommon.APPROVE);
					appMetaData.setWfStatusEn(adminApprovalVO.getStrStatusEn());
					appMetaData.setWfStatusAr(adminApprovalVO.getStrStatusAr());
					clientPersonAdminWF.updateCompleteStepVO(completeStepVO, adminApprovalVO.getStrStepName(), ConstantsCommon.WORK_FLOW_STATUS_APPROVE, adminApprovalVO.getStrUserNotification());
        		}else if(adminApprovalVO.getStrActionType().equals(ConstantsCommon.APPROVE)){
        		    	// If Approved by any other departments, send the approval message to the Ultimus alone
        			clientPersonAdminWF.updateCompleteStepVO(completeStepVO, adminApprovalVO.getStrStepName(), ConstantsCommon.WORK_FLOW_STATUS_APPROVE, adminApprovalVO.getStrUserNotification());
        		}else if(adminApprovalVO.getStrActionType().equals(ConstantsCommon.REJECT)){
        		    	// If Rejected, update the application request in DB as rejected and send the rejection message to the Ultimus
			    	UserReq userReq = this.userReqDomainObjectManager.findByApplicationId(adminApprovalVO.getStrApplicationId());
				userReq.setUReqStatus(ConstantsCommon.REJECT);
				this.userReqDomainObjectManager.update(userReq);
        		    appMetaData.setWfStatus(ConstantsCommon.REJECT);
        			appMetaData.setWfStatusEn(adminApprovalVO.getStrStatusEn());
        			appMetaData.setWfStatusAr(adminApprovalVO.getStrStatusAr());
        			clientPersonAdminWF.updateCompleteStepVO(completeStepVO, adminApprovalVO.getStrStepName(), ConstantsCommon.WORK_FLOW_STATUS_REJECT, adminApprovalVO.getStrUserNotification());
        		}
        		appMetaDataDomainObjectManager.update(appMetaData);
        		return completeStepVO;
        	}
        	
        	return null;		
	}
	/**
	 * createClientPerson
	 * @param adminApprovalVO
	 * @param clientPersonVO
	 */
	private void createClientPersonAdmin(AdminApprovalVO adminApprovalVO, ClientPersonVO clientPersonVO) {
		List<Service> services = findServicesToAdd(clientPersonVO.getStrTrakheesId());
		if(services != null){
			getClientPersonAdminServices(services);
		}
		/*************Storing the Client**********************/
		Client clientRegistered = this.clientDomainObjectManager.findByTrakheesId(clientPersonVO.getStrTrakheesId());
		Client client;		
		if(clientRegistered != null)
			client = clientRegistered;
		else
			client = new Client();
		getMapper().map(clientPersonVO, client);
		for(User user:client.getUsers()){
			user.setClient(client);
			user.setUStatus(ConstantsCommon.APPROVE);
		}
		Set<ClientServiceRole> clientServiceRoles = new HashSet<ClientServiceRole>();
		List<UserServiceSecurity> userServiceSecurities = new ArrayList<UserServiceSecurity>();
		getServicesAndRoles(services, clientServiceRoles, userServiceSecurities, clientRegistered, client);
		//if the client is already registered we don't need to set this
		if(clientRegistered == null) {
			if( clientServiceRoles.size() > 0 )
				client.setClientServiceRoles(clientServiceRoles);
		}
		if(client != null)
			clientDomainObjectManager.update(client);				
		/*************End Client Store**********************/
		/*************Storing User Service Security*********/
		for(UserServiceSecurity security: userServiceSecurities) {
			this.userServiceSecurityDomainObjectManager.add(security);
		}
	}
	/**
	 * getClientPersonAdminServices
	 * @param services
	 */
	private void getClientPersonAdminServices(List<Service> services) {
		// Start - Adding the default services like User Default Service and Company Admin Service
		Service userDefaultService = this.serviceDomainObjectManager.findUserDefaultService();
		
		if( userDefaultService != null )
			services.add(userDefaultService);
		
		// End - Adding the default services like User Default Service and Company Admin Service
	}

	/**
	 * getServicesAndRoles
         * @param userServiceSecurities 
         * @param clientServiceRoles 
         * @param services 
         * @param client 
         * @param clientRegistered 
	 * 
	 * @return
	 * List<UserServiceSecurity>
	 */
	private void getServicesAndRoles(List<Service> services, Set<ClientServiceRole> clientServiceRoles, List<UserServiceSecurity> userServiceSecurities, Client clientRegistered, Client client) {

		if (services != null) {

			for (Service service : services) {

				for (ServiceRole serviceRole : service.getServiceRoles()) {
					// if the client is already registered we don't need
					// to set this
					if (clientRegistered == null) {
						ClientServiceRole clientServiceRole = new ClientServiceRole();
						clientServiceRole.setServiceRole(serviceRole);
						clientServiceRole.setClient(client);

						clientServiceRoles.add(clientServiceRole);
					}

					// For User Service Security
					UserServiceSecurity userServiceSecurity = new UserServiceSecurity();
					userServiceSecurity.setServiceRole(serviceRole);
					userServiceSecurity.setUsSecurityUsername(client.getUsers().iterator().next().getUUsername());
					//userServiceSecurity.setUsSecurityUserType(client.getCType());
					userServiceSecurity.setUsSecurityUserType(client.getUsers().iterator().next().getUType());
					userServiceSecurities.add(userServiceSecurity);
				}
			}

		}
		
	}
	/**
	 * findServicesToAdd
	 * @param strTrakheesId
	 * @return
	 */
	private List<Service> findServicesToAdd(String strTrakheesId) {
		List<SCClientPersonRoleVO> personRoleVOs = null;
		List<Service> services = new ArrayList<Service>();
		if (strTrakheesId != null) {
			personRoleVOs = this.singleClientPersonDomainObjectManager.findSCPersonRoleByTrakheesId(strTrakheesId);
		}
		if(personRoleVOs!=null)					
		{
			List<Long> list = new ArrayList<Long>();
			
			for(SCClientPersonRoleVO scClientPersonRoleVO: personRoleVOs)
			{
				list.add(scClientPersonRoleVO.getCrId());							
			}
			
			if(list.size() > 0)
				services = this.serviceDomainObjectManager.findServiceByServiceRoleIds(list, ConstantsCommon.SERVICE_DEFAULT);
			
		}
		return services;
	}
	/**
	 * getCompleteStepVO
	 * @param adminApprovalVO
	 * @param clientPersonVO
	 * @param appMetaData
	 * @return
	 */
	private ClientAdminCompleteStepVO getCompleteStepVO(
			AdminApprovalVO adminApprovalVO, ClientPersonVO clientPersonVO,
			AppMetaData appMetaData) {
		ClientAdminCompleteStepVO completeStepVO = new ClientAdminCompleteStepVO();
		completeStepVO.setgApplicationID(adminApprovalVO.getStrApplicationId());
		completeStepVO.setgClientReference(appMetaData.getClientRefNo());
		completeStepVO.setStrStepName(adminApprovalVO.getStrStepName());
		completeStepVO.setStrUserName(adminApprovalVO.getStrUserName());
		completeStepVO.setgName(clientPersonVO.getUserVOs().get(0).getStrUserName());
		completeStepVO.setgEmail(clientPersonVO.getUserVOs().get(0).getStrEmail());	
		completeStepVO.setnIncidentNumber(Integer.parseInt(adminApprovalVO.getStrIncidentNo()));
		return completeStepVO;
	}
	/**
	 * undoReviewRegisterClientPerson
	 * @param adminApprovalVO
	 * @param clientPersonVO
	 */
	@Transactional
	public void undoReviewRegisterClientPerson(AdminApprovalVO adminApprovalVO, ClientPersonVO clientPersonVO) {
		AppMetaData appMetaData = appMetaDataDomainObjectManager.findByPrimaryKeyProcessAndStepCode(adminApprovalVO
				.getStrApplicationId().toString(), adminApprovalVO.getProcessCode().getProcessCode(), adminApprovalVO.getStrStepName());

		if (appMetaData != null) {
			adminApprovalVO.setStrApplnStatus(ConstantsCommon.IN_PROGRESS);
			getMapper().map(adminApprovalVO, appMetaData);
			
			this.appMetaDataDomainObjectManager.undoUpdateAppMetaDataWithActionTaken(appMetaData, adminApprovalVO.getStrStepName());

			if(clientPersonAdminWF.getFinDeptStepName().equalsIgnoreCase(adminApprovalVO.getStrStepName()) && adminApprovalVO.getStrActionType().equals(ConstantsCommon.APPROVE)){
			    	// If Approved by Finance department, send the approval message to the Ultimus alone and update the application request in the DB as approved
			    	undoCreateClientPersonAdmin(adminApprovalVO, clientPersonVO);
			}
			if(clientPersonAdminWF.getFinDeptStepName().equalsIgnoreCase(adminApprovalVO.getStrStepName()) || adminApprovalVO.getStrActionType().equals(ConstantsCommon.REJECT)){
		    	UserReq userReq = this.userReqDomainObjectManager.findByApplicationId(adminApprovalVO.getStrApplicationId());
				userReq.setUReqStatus(ConstantsCommon.IN_PROGRESS);
				this.userReqDomainObjectManager.update(userReq);
				
			}
			appMetaData.setWfStatus(ConstantsCommon.IN_PROGRESS);
			appMetaData.setWfStatusEn(adminApprovalVO.getStrInProgressStatusEn());
			appMetaData.setWfStatusAr(adminApprovalVO.getStrInProgressStatusAr());
			appMetaDataDomainObjectManager.update(appMetaData);
		}
	}
	/**
	 * undoCreateClientPersonAdmin
	 * @param adminApprovalVO
	 * @param clientPersonVO
	 */
	private void undoCreateClientPersonAdmin(AdminApprovalVO adminApprovalVO, ClientPersonVO clientPersonVO) {
        	List<Service> services = findServicesToAdd(clientPersonVO.getStrTrakheesId());
        	if(services != null){
        		getClientPersonAdminServices(services);
        		Client client = this.clientDomainObjectManager.findByTrakheesId(clientPersonVO.getStrTrakheesId());
        		if(client != null){
        		    	undoAddServicesAndRoles(services, client);
        			Set<User> users = client.getUsers();
        			for(User user : users){
        			    userDomainObjectManager.delete(user);
        			}
        			client.getUsers().clear();
        		}
        	}
	}
	/**
	 * undoAddServicesAndRoles
	 * @param services
	 * @param client
	 */
	private void undoAddServicesAndRoles(List<Service> services, Client client) {

		if (services != null) {
			
			Set<UserServiceSecurity> ussSet = new HashSet<UserServiceSecurity>();

			for (Service service : services) {

				for (ServiceRole serviceRole : service.getServiceRoles()) {
					// if the client is already registered we don't need
					// to set this
					ClientServiceRole clientServiceRole = new ClientServiceRole();
					clientServiceRole.setServiceRole(serviceRole);
					clientServiceRole.setClient(client);
					List<ClientServiceRole> clientServiceRoles = this.clientDomainObjectManager.findClientServiceRole(clientServiceRole);
					if(clientServiceRoles != null){
						for(ClientServiceRole csr : clientServiceRoles){
							this.clientDomainObjectManager.delete(csr);
						}
					}

					// For User Service Security
					UserServiceSecurity userServiceSecurity = new UserServiceSecurity();
					userServiceSecurity.setServiceRole(serviceRole);
					userServiceSecurity.setUsSecurityUsername(client.getUsers().iterator().next().getUUsername());
					//userServiceSecurity.setUsSecurityUserType(client.getCType());
					userServiceSecurity.setUsSecurityUserType(client.getUsers().iterator().next().getUType());
					List<UserServiceSecurity> ussList = this.userServiceSecurityDomainObjectManager.findUserServiceSecurity(userServiceSecurity);
					ussSet.addAll(ussList);
				}
			}
			for(UserServiceSecurity uss : ussSet){
				this.userServiceSecurityDomainObjectManager.delete(uss);
			}

		}
		
	}
}
